﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using SvgNet.SvgGdi;

namespace Graphix
{
    public class ChartImpl : IChart
    {
        private float Ymax = 90.0f;
        private float Ymin = 0.0f;
        private Color _backgroundColor;
        private Bitmap _bmp;
        private ChartBorder _border;
        private Chart _chart;
        private List<Curve> _curves;
        private IGraphics _graphics;

        private Dictionary<string, List<PositionedLine>> _lines;
        private Dictionary<string, List<PositionedString>> _positioned;

        //todo: remove added - possibly change to MoveAll
        private List<string> added = new List<string>();

        #region Excluded colors

        private readonly Color[] _exludedColors = new[]
                                                      {
                                                          Color.AliceBlue,
                                                          Color.Aquamarine,
                                                          Color.Azure,
                                                          Color.Beige,
                                                          Color.AntiqueWhite,
                                                          Color.Bisque,
                                                          Color.BlanchedAlmond,
                                                          Color.Cornsilk,
                                                          Color.Empty,
                                                          Color.FloralWhite,
                                                          Color.GhostWhite,
                                                          Color.Ivory,
                                                          Color.LavenderBlush,
                                                          Color.LemonChiffon,
                                                          Color.LightGoldenrodYellow,
                                                          Color.LightYellow,
                                                          Color.Linen,
                                                          Color.MintCream,
                                                          Color.MistyRose,
                                                          Color.Moccasin,
                                                          Color.NavajoWhite,
                                                          Color.OldLace,
                                                          Color.PaleGoldenrod,
                                                          Color.PapayaWhip,
                                                          Color.PeachPuff,
                                                          Color.SeaShell,
                                                          Color.Snow,
                                                          Color.White,
                                                          Color.WhiteSmoke,
                                                          Color.Transparent
                                                      };

        #endregion

        public ChartImpl(int chartWidth, int chartHeight, Brush chartBorderColors)
        {
            _bmp = new Bitmap(chartWidth, chartHeight);
            _graphics = new GdiGraphics(Graphics.FromImage(_bmp));
            //_graphics = new SvgGraphics();
            _border = new ChartBorder(0, 0, chartWidth, chartHeight);
            _chart = new Chart(new RectangleF(0, 0, chartWidth, chartHeight), chartBorderColors);

            _border.Changed += _border_Changed;


            _lines = new Dictionary<string, List<PositionedLine>>();
            _positioned = new Dictionary<string, List<PositionedString>>();
            _curves = new List<Curve>();
        }

        #region IChart Members

        public IChart SetTitle(string title, Font font, Horizontal position = Horizontal.Center,
                               Horizontal alignment = Horizontal.Center, float offset = 0,
                               Brush color = null)
        {
            SizeF size = _graphics.MeasureString(title, font);

            StringFormat sf = new StringFormat {Alignment = StringAlignment.Center};

            float x = _chart.Size.Width/2 + _chart.Size.Left;
            switch (position)
            {
                case Horizontal.Left:
                    x = _chart.Size.Left;
                    if (size.Width/2 + x > _chart.Size.X)
                    {
                        _chart.Size.X += (int) (size.Width/2 - _chart.Size.X);
                        x = _chart.Size.Left;
                    }
                    break;
                case Horizontal.Right:
                    x = _chart.Size.Right;
                    break;
            }
            x += _chart.Size.Width*offset;

            float right = x + size.Width/2;
            if (_border.Right < right)
                _border.Right = right;

            if (size.Height > _chart.Size.Y)
            {
                _chart.Size.Y += (size.Height - _chart.Size.Y);
                _border.Top = _chart.Size.Y;
            }


            PositionedString ps =
                new PositionedString(x, 0, size.Width, size.Height, title, font, color);

            ps.HorizontalAlignment = alignment;
            ps.Format = sf;

            AddPositionedString("Title", ps);

            return this;
        }

        public IChart AddInformation(string info, Font font, Brush color = null)
        {
            return DrawMultipleLinesString(info, new PointF(0, _chart.Size.Y), Orientation.Vertical,
                                           font, "Information", true, color: color);
        }

        public IChart SetXTitle(string title, Font font, Brush color = null)
        {
            SizeF size = _graphics.MeasureString(title, font);
            PositionedString ps = new PositionedString(_chart.Size.Left + _chart.Size.Width/2,
                                                       _border.Bottom, size.Width, size.Height,
                                                       title, font, color);
            ps.Format = new StringFormat {Alignment = StringAlignment.Center};
            _border.Bottom += size.Height;
            AddPositionedString("XTitle", ps);
            return this;
        }

        public IChart SetYTitle(string title, Font font, Brush color = null, bool ignoreMove = true)
        {
            //todo: orientation

            SizeF size = _graphics.MeasureString(title, font);

            float x = _chart.Size.Left - size.Height;
            if (!ignoreMove)
            {
                if (_border.Left - size.Height >= _chart.Size.X - size.Height)
                {
                    MoveAll("YTitle", size.Height + _chart.Size.X);
                }
            }


            PositionedString ps =
                new PositionedString(x, _chart.Size.Y + _chart.Size.Width/2, size.Width, size.Height,
                                     title, font, color);

            ps.HorizontalAlignment = Horizontal.Center;
            ps.Rotation = 270;

            AddPositionedString("YTitle", ps);
            return this;
        }

        public IChart AddTitleMarginMarker(string title, double position, Pen linePen,
                                           Font font, Brush textColor = null,
                                           bool moreMarkers = true)
        {
            double x = _chart.Size.Width*position + _chart.Size.X;
            float move = 0;
            if (!moreMarkers)
                move = 2;
            DrawMultipleLinesString(title, new PointF((float) x, _chart.Size.Y),
                                    Orientation.Horizontal, font,
                                    "TitleMarker", true, moreMarkers, true, move, textColor);

            if (Math.Abs(x - 0) > 0.01 && Math.Abs(x - 1) > 0.01)
            {
                PositionedLine pl = new PositionedLine((float) x, _chart.Size.Top, (float) x,
                                                       _chart.Size.Bottom, linePen);
                AddPositionedLine("TitleMarker", pl);
            }

            return this;
        }

        public IChart AddTitleMarginMarkers(StringPoint[] array)
        {
            throw new NotImplementedException();
        }

        //Y
        public IChart SetXLabels(string[] labels, Pen linePen, Font font, Brush textColor = null,
                                 double min = 0, double max = 1,
                                 double tick = 0.1, Orientation orientation = Orientation.Horizontal)
        {
            if (min >= max)
                throw new ArgumentException("Wartosc min nie moze byc wieksza, ani równa od max");
            if (min < 0 || min > 1)
                throw new ArgumentException("Niepoprawna wartosc min");
            if (max < 0 || max > 1)
                throw new ArgumentException("Niepoprawna wartosc max");
            if (tick < 0 || tick > 1)
                throw new ArgumentException("Niepoprawna wartosc tick");

            min = _chart.Size.Left + min*_chart.Size.Width;
            max = _chart.Size.Left + max*_chart.Size.Width;

            tick = _chart.Size.Width/(labels.Length);

            float y = _chart.Size.Bottom;
            for (int i = 0; i < labels.Length; i++)
            {
                string label = labels[i];

                SizeF size = _graphics.MeasureString(label, font);

                //todo: add _border.Bottom i _border.Right
                float x = (float) (min + tick*i);

                //todo: inny ogranicznik
                //if (i > 0 && i < labels.Length - 1)
                //{
                PositionedLine pl = new PositionedLine(x, _chart.Size.Top, x, y, linePen);

                AddPositionedLine("XLabel", pl);
                //}

                PositionedString ps =
                    new PositionedString(x, y + 2, size.Width, size.Height, label, font, textColor);
                ps.Format = new StringFormat {Alignment = StringAlignment.Center};
                ps.HorizontalAlignment = Horizontal.Center;

                if (orientation == Orientation.Vertical)
                    ps.Rotation = 270;

                AddPositionedString("XLabel", ps);

                _border.Bottom = _chart.Size.Bottom + size.Height + 2;
            }
            return this;
        }

        public IChart SetXLabels(double min, double max,
                                 Orientation orientation = Orientation.Horizontal)
        {
            throw new NotImplementedException();
        }

        public IChart SetX2Labels(string[] labels, Font font, Brush textColor = null,
                                  double min = 0.0, double max = 1.0,
                                  Orientation orientation = Orientation.Horizontal)
        {
            double tick = (max - min)*_chart.Size.Width/(labels.Length - 1);
            min = _chart.Size.Width*min;
            for (int i = 0; i < labels.Length; i++)
            {
                double x = tick*i + min + _chart.Size.X;
                SizeF size = _graphics.MeasureString(labels[i], font);

                if (!added.Contains("X2Labels") &&
                    _chart.Size.Contains(new RectangleF((float) x, _chart.Size.Y, size.Height,
                                                        size.Width)))
                {
                    added.Add("X2Labels");
                    _border.Top = _chart.Size.Y;
                    _chart.Size.Y += (size.Height);
                }

                /*
                if (_chart.Size.Contains(new Rectangle((int) x, (int) _border.Top, (int) size.Height, (int) size.Width)))
                {
                    _border.Top = _chart.Size.Y;
                    _chart.Size.Y += (int) (size.Height);
                }
                */

                PositionedString ps =
                    new PositionedString((float) x, _border.Top, size.Width, size.Height, labels[i],
                                         font, textColor) {HorizontalAlignment = Horizontal.Center};

                AddPositionedString("X2Labels", ps);
            }
            return this;
        }

        public IChart SetX2Labels(double min, double max,
                                  Orientation orientation = Orientation.Horizontal)
        {
            throw new NotImplementedException();
        }

        public IChart SetYLabels(string[] labels, Pen linePen, Font font, Brush textColor = null,
                                 Orientation orientation = Orientation.Horizontal)
        {
            string longest = labels.OrderByDescending(x => x.Length).First();
            SizeF size = _graphics.MeasureString(longest, font);

            float length = size.Width;
            if (orientation == Orientation.Vertical)
                length = size.Height;

            if (_border.Left - length >= _chart.Size.X - length)
            {
                MoveAll("YTitle", length + _chart.Size.X);
            }

            float tick = _chart.Size.Height/((float) labels.Length - 1);

            for (int i = 0; i < labels.Length; i++)
            {
                string label = labels[i];

                size = _graphics.MeasureString(label, font);
                float x = _chart.Size.Left - size.Height;
                float y = _chart.Size.Bottom - tick*i;

                StringFormat sf = new StringFormat();

                if (i == 0)
                {
                    y -= size.Height;
                    //sf.LineAlignment = StringAlignment.Far;
                }
                else if (i < labels.Length - 1)
                {
                    PositionedLine pl = new PositionedLine(_chart.Size.Left, y, _chart.Size.Right, y,
                                                           linePen);

                    AddPositionedLine("YTitle", pl);
                    //sf.LineAlignment = StringAlignment.Center;
                    y -= size.Height/2;
                }

                PositionedString ps =
                    new PositionedString(x, y, size.Width, size.Height, label, font, textColor);
                //ps.Format = sf;
                //ps.Format = new StringFormat();
                //ps.HorizontalAlignment = Horizontal.Center;

                if (orientation == Orientation.Vertical)
                    ps.Rotation = 270;

                AddPositionedString("YTitle", ps);
            }
            return this;
        }

        public IChart SetYLabels(double min, double max,
                                 Orientation orientation = Orientation.Horizontal)
        {
            throw new NotImplementedException();
        }

        public IChart SetY2Labels(string[] labels, Font font, Brush textColor = null, double min = 0,
                                  double max = 1, Orientation orientation = Orientation.Horizontal)
        {
            //font = new Font(font.FontFamily, 6);
            min = _chart.Size.Top + min*_chart.Size.Height;
            max = _chart.Size.Top + max*_chart.Size.Height;

            double tick = (max - min)/(labels.Length - 1);

            //todo: add _border.Bottom i _border.Right
            float x = (_chart.Size.Right);

            for (int i = 0; i < labels.Length; i++)
            {
                float y = (float) (max - tick*i);
                string label = labels[i];

                SizeF size = _graphics.MeasureString(label, font);

                float check = size.Height + 2 + _chart.Size.Right;
                if (_border.Right < check)
                {
                    _border.Right = check;
                }

                PositionedString ps =
                    new PositionedString(x + 2, y + 2, size.Width, size.Height, label, font,
                                         textColor);
                ps.Format = new StringFormat {Alignment = StringAlignment.Center};
                ps.HorizontalAlignment = Horizontal.Center;

                ps.Rotation = -90;

                AddPositionedString("Y2Label", ps);
            }

            return this;
        }

        public IChart SetY2Labels(double min, double max,
                                  Orientation orientation = Orientation.Horizontal)
        {
            throw new NotImplementedException();
        }

        public IChart DrawCurve(PointF[] points, string objectName,
                                ValueType valueTypes = ValueType.None, Color color = default(Color),
                                float penSize = 1)
        {
            if (valueTypes.HasFlag(ValueType.XPercentage) && valueTypes.HasFlag(ValueType.XValues))
            {
                throw new ArgumentException("Niepoprawne flagi dla wartości X");
            }
            if (valueTypes.HasFlag(ValueType.YPercentage) && valueTypes.HasFlag(ValueType.YValues))
            {
                throw new ArgumentException("Niepoprawne flagi dla wartości Y");
            }

            if (color == default(Color))
            {
                Color c = new Color();
                PropertyInfo[] properties = c.GetType().GetProperties();

                for (int i = 0; i < properties.Length; i++)
                {
                    PropertyInfo property = properties[i];
                    Color temp = property.GetValue(c, null) is Color
                                     ? (Color) property.GetValue(c, null)
                                     : default(Color);
                    if (!_exludedColors.Contains(temp) &&
                        !_curves.Any(x => x.Pen.Color == temp) &&
                        temp != default(Color))
                    {
                        color = temp;
                        break;
                    }
                }

                if (color == default(Color))
                {
                    throw new ArgumentException(
                        "Brak ustawionych kolorow do wyboru, konieczne stworzenie nowego koloru.");
                }
            }

            foreach (ValueType flag in valueTypes.GetFlags())
            {
                switch (flag)
                {
                    case ValueType.None:
                        break;
                    case ValueType.XValues:
                        //todo zmienic na X
                        if (points.Any(x => x.X > Ymax || x.X < Ymin))
                        {
                            throw new ArgumentException(
                                string.Format(
                                    "Dla tego typu danych, dopuszczalne wartosci znajduja sie w przedziale '{0} <= x >= {1}'",
                                    Ymin, Ymax));
                        }
                        points = RecalculateXValues(points);
                        break;
                    case ValueType.YValues:
                        if (points.Any(x => x.Y > Ymax || x.Y < Ymin))
                        {
                            throw new ArgumentException(
                                string.Format(
                                    "Dla tego typu danych, dopuszczalne wartosci znajduja sie w przedziale '{0} <= x >= {1}'",
                                    Ymin, Ymax));
                        }
                        points = RecalculateYValues(points);
                        break;
                    case ValueType.XPercentage:
                        if (points.Any(x => x.X > 1 || x.X < 0))
                        {
                            throw new ArgumentException(
                                "Dla tego typu danych, dopuszczalne wartosci znajduja sie w przedziale '0 <= x >= 1'");
                        }
                        points = RecalculateXPercents(points);
                        break;
                    case ValueType.YPercentage:
                        if (points.Any(x => x.Y > 1 || x.Y < 0))
                        {
                            throw new ArgumentException(
                                "Dla tego typu danych, dopuszczalne wartosci znajduja sie w przedziale '0 <= x >= 1'");
                        }
                        points = RecalculateYPercents(points);
                        break;
                    default:
                        throw new ArgumentException("Niepoprawna wartosc dla flagi.");
                }
            }

            _curves.Add(new Curve(objectName, new Pen(color, penSize), points));

            return this;
        }

        /// <summary>
        /// </summary>
        /// <returns> </returns>
        public string Save()
        {
            DrawChart();
            if (_graphics is SvgGraphics)
                return ((SvgGraphics) _graphics).WriteSVGString();

            return string.Empty;
        }

        public IChart SetBackground(Color backgroundColor)
        {
            _backgroundColor = backgroundColor;
            return this;
        }

        public void SaveToStream(Stream ms, ImageFormat format)
        {
            Save();
            //Just in case,but not for huge images
            if (_bmp.Height <= 1500 && _bmp.Width <= 1500)
                Helpers.AutoCrop(_bmp, _backgroundColor).Save(ms, format);
            else
                _bmp.Save(ms, ImageFormat.Png);
        }

        public void SaveToFile(string filename, ImageFormat format)
        {
            Save();
            _bmp.Save(filename, format);
        }

        #endregion

        private void AddPositionedString(string name, PositionedString ps)
        {
            if (!_positioned.ContainsKey(name))
            {
                _positioned.Add(name, new List<PositionedString> {ps});
            }
            else
            {
                List<PositionedString> temp = _positioned[name];
                if (temp != null)
                {
                    temp.Add(ps);
                }
            }
        }

        private void AddPositionedLine(string name, PositionedLine single)
        {
            if (!_lines.ContainsKey(name))
            {
                _lines.Add(name, new List<PositionedLine> {single});
            }
            else
            {
                List<PositionedLine> temp = _lines[name];
                if (temp != null)
                {
                    temp.Add(single);
                }
            }
        }

        private IChart DrawMultipleLinesString(string text, PointF positionBegin,
                                               Orientation orientation, Font usedFont,
                                               string elementName, bool move = false,
                                               bool reverse = false, bool forceMove = false,
                                               float additional = 0,
                                               Brush color = null)
        {
            if (orientation == Orientation.Vertical)
            {
                StringFormat sf = new StringFormat();
                sf.Alignment = StringAlignment.Near;
                sf.Trimming = StringTrimming.EllipsisCharacter;

                string[] lines = text.Split(new[] {"\r\n", "\n"}, StringSplitOptions.None);

                for (int i = 0; i < lines.Length; i++)
                {
                    SizeF size = _graphics.MeasureString(lines[i], usedFont);

                    if (move)
                    {
                        if (size.Width > _chart.Size.X)
                        {
                            MoveAll(elementName, size.Width);
                        }
                    }

                    PositionedString ps =
                        new PositionedString(0, positionBegin.Y, size.Width, size.Height, lines[i],
                                             usedFont) {HorizontalAlignment = Horizontal.Left};

                    AddPositionedString(elementName, ps);

                    positionBegin.Y += size.Height;
                }
            }
            else
            {
                StringFormat sf = new StringFormat();
                sf.Alignment = StringAlignment.Near;
                sf.Trimming = StringTrimming.EllipsisCharacter;

                string[] lines = text.Split(new[] {"\r\n", "\n"}, StringSplitOptions.None);

                bool moved = false;
                float sum = 0;
                for (int i = 0; i < lines.Length; i++)
                {
                    SizeF size = _graphics.MeasureString(lines[i], usedFont);
                    if (move ||
                        _chart.Size.Contains(new Rectangle((int) positionBegin.X,
                                                           (int) positionBegin.Y, (int) size.Height,
                                                           (int) size.Width)))
                    {
                        moved = true;
                        MoveAll(elementName, y: size.Height, force: forceMove);
                    }

                    PositionedString ps =
                        new PositionedString(positionBegin.X, _border.Top, size.Width, size.Height,
                                             lines[i], usedFont)
                            {HorizontalAlignment = Horizontal.Center};

                    AddPositionedString(elementName, ps);

                    positionBegin.Y += size.Height;
                    sum += size.Height;
                }
                if (moved && reverse)
                {
                    MoveAll(elementName, y: -sum, force: forceMove);
                }
                if (additional != 0)
                {
                    MoveAll(elementName, y: additional);
                }
            }
            return this;
        }

        private void MoveAll(string element, float x = 0, float y = 0, bool force = true)
        {
            if (x == 0 && y == 0)
            {
                return;
            }

            if (_border.Bottom == 0)
            {
                _border.Bottom = _chart.Size.Bottom;
            }
            if (Math.Abs(x - 0) > 0.001)
            {
                x -= _chart.Size.X;
                _chart.Size.X += x;
                _border.Left = _chart.Size.X;
                foreach (KeyValuePair<string, List<PositionedString>> pair in _positioned)
                {
                    if (pair.Key == element)
                    {
                        continue;
                    }
                    foreach (PositionedString t in pair.Value)
                    {
                        if (_chart.Size.X - x <= t.XStart)
                        {
                            t.XStart += x;

                            if (t.XEnd > 0)
                                t.XEnd += x;
                        }
                    }
                }


                foreach (KeyValuePair<string, List<PositionedLine>> pair in _lines)
                {
                    if (pair.Key == element && !force)
                    {
                        continue;
                    }
                    for (int i = 0; i < pair.Value.Count; i++)
                    {
                        PositionedLine t = pair.Value[i];
                        if (_chart.Size.X - x <= t.XStart)
                        {
                            t.XStart += x;
                            t.XEnd += x;
                        }
                        if (Math.Abs(t.XStart - _chart.Size.Left) < 1 ||
                            Math.Abs(t.XStart - _chart.Size.Right) < 1)
                        {
                            pair.Value.RemoveAt(i);
                        }
                    }
                }
            }

            if (Math.Abs(y - 0) > 0.001)
            {
                _chart.Size.Y += y;
                _border.Top += y;
                foreach (KeyValuePair<string, List<PositionedString>> pair in _positioned)
                {
                    if (pair.Key == element)
                    {
                        continue;
                    }
                    foreach (PositionedString t in pair.Value)
                    {
                        if (_chart.Size.Y - y <= t.YStart)
                        {
                            t.YStart += y;

                            if (t.YEnd > 0)
                                t.YEnd += y;
                        }
                    }
                }
                foreach (KeyValuePair<string, List<PositionedLine>> pair in _lines)
                {
                    if (pair.Key == element && !force)
                    {
                        continue;
                    }
                    for (int i = 0; i < pair.Value.Count; i++)
                    {
                        PositionedLine t = pair.Value[i];
                        if (_chart.Size.Y - y <= t.YStart)
                        {
                            t.YStart += y;
                            t.YEnd += y;
                        }
                        if (Math.Abs(t.XStart - _chart.Size.X) < 1 ||
                            Math.Abs(t.XStart - _chart.Size.Right) < 1)
                        {
                            pair.Value.RemoveAt(i);
                        }
                        else
                        {
                            if (Math.Abs(t.YEnd - t.YStart - _chart.Size.Height) < 1)
                            {
                                t.YStart = _chart.Size.Top;
                                t.YEnd = _chart.Size.Bottom;
                            }
                        }
                    }
                }
            }
        }

        private PointF[] RecalculateXPercents(PointF[] points)
        {
            for (int i = 0; i < points.Length; i++)
            {
                points[i].X = points[i].X*_chart.Size.Width + _chart.Size.Left;
            }
            return points;
        }

        private PointF[] RecalculateXValues(PointF[] points)
        {
            float width = _chart.Size.Width;
            float addon = _chart.Size.Left;
            //todo zmienic na X
            float diff = Ymax - Ymin;
            for (int i = 0; i < points.Length; i++)
            {
                points[i].X = width - (points[i].X/diff)*width + addon;
            }
            return points;
        }

        private PointF[] RecalculateYValues(PointF[] points)
        {
            float height = _chart.Size.Height;
            float addon = _chart.Size.Top;
            float diff = Ymax - Ymin;
            for (int i = 0; i < points.Length; i++)
            {
                points[i].Y = height - (points[i].Y/diff)*height + addon;
            }
            return points;
        }

        private PointF[] RecalculateYPercents(PointF[] points)
        {
            for (int i = 0; i < points.Length; i++)
            {
                points[i].X = points[i].Y*_chart.Size.Height + _chart.Size.Top;
            }
            return points;
        }


        public IHtmlString SaveAsp()
        {
            return MvcHtmlString.Create(Save());
        }

        private void DrawChart()
        {
            //Setup Background
            _graphics.Clear(_backgroundColor);

            DrawChartBorders();

            DrawStrings();

            DrawLines();

            DrawCurves();

            DrawChartBorders();
        }

        private void DrawChartBorders()
        {
            _graphics.DrawRectangle(new Pen(_chart.Color), _chart.Size.X, _chart.Size.Y,
                                    _chart.Size.Width,
                                    _chart.Size.Height);
        }

        private void DrawStrings()
        {
            foreach (KeyValuePair<string, List<PositionedString>> single in _positioned)
            {
                foreach (PositionedString singleString in single.Value)
                {
                    StringFormat sf = new StringFormat();

                    switch (singleString.HorizontalAlignment)
                    {
                        case Horizontal.Left:
                            sf.Alignment = StringAlignment.Near;
                            break;
                        case Horizontal.Right:
                            sf.Alignment = StringAlignment.Far;
                            break;
                        default:
                            sf.Alignment = StringAlignment.Center;
                            break;
                    }

                    if (Math.Abs(singleString.Rotation - 0) > 0.1)
                    {
                        if (singleString.Format != null)
                            sf = singleString.Format;

                        _graphics.RotateTransform(singleString.Rotation, MatrixOrder.Append);
                        _graphics.TranslateTransform(singleString.XStart, singleString.YStart,
                                                     MatrixOrder.Append);
                        _graphics.DrawString(singleString.Text, singleString.Font,
                                             singleString.Color, 0, 0, sf);
                        _graphics.ResetTransform();
                    }
                    else
                    {
                        _graphics.DrawString(singleString.Text, singleString.Font,
                                             singleString.Color, singleString.XStart,
                                             singleString.YStart, sf);
                    }
                }
            }
        }

        private void DrawLines()
        {
            foreach (KeyValuePair<string, List<PositionedLine>> line in _lines)
            {
                foreach (PositionedLine one in line.Value)
                {
                    _graphics.DrawLine(one.Pen, one.XStart, one.YStart, one.XEnd, one.YEnd);
                }
            }
        }

        private void DrawCurves()
        {
            foreach (Curve curve in _curves)
            {
                _graphics.DrawCurve(curve.Pen, curve.Points);
            }
        }

        private void _border_Changed(object sender, ChangeEventArgs e)
        {
            float right = e.Right;
            float bottom = e.Bottom;

            if (right == 0)
                right = _chart.Size.Width;
            if (bottom == 0)
                bottom = _chart.Size.Height;
            
            if(right < _border.Right)
                right = _border.Right;

            if(bottom < _border.Bottom)
                bottom = _border.Bottom;

            var rect = new Rectangle(0, 0, (int) right, (int) bottom);

            if (_bmp.Width < rect.Width || _bmp.Height < rect.Height)
            {
                Bitmap newImage = new Bitmap(rect.Width, rect.Height, _bmp.PixelFormat);
                using (Graphics g = Graphics.FromImage(newImage))
                {
                    g.DrawImage(_bmp, 0, 0, right, bottom);
                }
                _bmp = new Bitmap(newImage);
                _graphics = new GdiGraphics(Graphics.FromImage(_bmp));
            }
        }
    }
}